#pragma once
//#include "oj_model.hpp"
#include "oj_view.hpp"
#include "../comm/util.hpp"
#include "../comm/httplib.h"
#include <mutex>
#include <fstream>
#include <json/json.h>
#include <algorithm>

namespace ns_control
{
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_util;
    using namespace httplib;

    struct machine
    {
        std::string ip; //主机IP
        uint64_t port;  //主机端口号
        uint64_t load;  //主机负载
        std::mutex* mtx; 

        machine() : ip(""), port(0), load(0), mtx(nullptr)
        {}

        //提升主机负载
        void IncLoad()
        {
            if(mtx)
                mtx->lock();
            ++load;
            if(mtx)
                mtx->unlock();
        }
        //减少主机负载
        void DecLoad()
        {
            if(mtx)
                mtx->lock();
            --load;
            if(mtx)
                mtx->unlock();
        }

        uint64_t GetLoad()
        {
            if(mtx)
                mtx->lock();
            uint64_t Load = load;
            if(mtx)
                mtx->unlock();

            return Load; 
        }

        void ResetLoad()
        {
            if(mtx)
                mtx->lock();
            load = 0;
            if(mtx)
                mtx->unlock();
        }
    };

    const std::string service_conf = "./conf/machine.conf";

    class LoadBalance
    {
    private:
        std::vector<machine> machine_list; //下标代表主机编号
        std::vector<int> onlines;
        std::vector<int> offlines;
        std::mutex mtx;
    public:
        LoadBalance()
        {
            assert(LoadConf(service_conf));
            LOG(INFO) << "加载 " << service_conf << " success" << std::endl;
        }
        ~LoadBalance()
        {}

        bool LoadConf(const std::string& machine_conf)
        {
            std::ifstream ifs(machine_conf);
            if(!ifs.is_open())
            {
                LOG(FATAL) << "打开文件失败" << std::endl;
                return false;
            }
            std::string line;
            while(std::getline(ifs, line))
            {
                std::vector<std::string> mach;
                const std::string sep = ":";
                StringUtil::SplitString(line, &mach, sep);
                if(mach.size() != 2)
                {
                    LOG(WARNING) << "加载文件失败" << std::endl;
                    continue;
                }
                machine m;
                m.ip = mach[0];
                m.port = atoi(mach[1].c_str());
                m.load = 0;
                m.mtx = new std::mutex();

                onlines.push_back(machine_list.size());
                machine_list.push_back(m);
            }
            ifs.close();
            return true;
        }

        bool SmartChoice(int* id, machine** m)
        {
            mtx.lock();
            int sz = onlines.size();
            if(sz == 0)
            {
                mtx.unlock();
                LOG(FATAL) << "所有的主机都下线了，请运维的同事进行查看" << std::endl;
                return false;
            }
            *id = onlines[0];
            *m = &machine_list[onlines[0]];
            uint64_t min_load = machine_list[*id].GetLoad();
            for(int i = 1;i < sz;++i)
            {
                uint64_t cur_load = machine_list[onlines[i]].GetLoad();
                if(min_load > cur_load)
                {
                    min_load = cur_load;
                    *id = onlines[i];
                    *m = &machine_list[onlines[i]];
                }   
            }
            mtx.unlock();
            return true;
        }

        void OnlineMachine()
        {
            mtx.lock();
            onlines.insert(onlines.end(), offlines.begin(), offlines.end());
            offlines.erase(offlines.begin(), offlines.end());
            mtx.unlock();
        }

        void OfflineMachine(int id)
        {
            mtx.lock();
            auto iter = onlines.begin();
            for(;iter != onlines.end();++iter)
            {
                if(*iter == id)
                {
                    machine_list[id].ResetLoad();
                    onlines.erase(iter);
                    offlines.push_back(id);
                    break;
                }
            }
            mtx.unlock();
        }

        void ShowMachine()
        {
            mtx.lock();
            std::cout << "在线主机列表：" << std::endl;
            for(const auto& id : onlines)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            std::cout << "离线主机列表：" << std::endl;
            for(const auto& id : offlines)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            mtx.unlock();
        }
    };

    class control
    {
    private:
        model _model;
        view _view;
        LoadBalance _load_balance;
    public:
        control()
        {}
        ~control()
        {}

        void RecoveryMachine()
        {
            _load_balance.OnlineMachine();
        }

        bool AllQuestions(std::string* html)
        {
            std::vector<struct Question> out;
            bool ret = true;
            if(_model.GetAllQuestions(&out))
            {
                std::sort(out.begin(), out.end(), [](struct Question& q1, struct Question& q2){
                    return atoi(q1.number.c_str()) < atoi(q2.number.c_str());
                }); 
                _view.AllExpandHtml(out, html);
            }
            else
            {
                *html = "获取题目列表失败";
                ret = false;
            }

            return ret;
        }

        bool Question(const std::string number, std::string* html)
        {
            struct Question out;
            bool ret = true;
            if(_model.GetOneQuestion(number, &out))
            {
                _view.OneExpandHtml(out, html);
            }
            else
            {
                ret = false;
                *html = "获取指定题目 " + number + " 失败";
            }
        }

        void Judge(const std::string& number, const std::string& in_json, std::string* out_json)
        {
            //根据题目编号，获取题目内容
            struct Question q;
            _model.GetOneQuestion(number, &q);
            //反序列化
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);
            //重新拼接代码
            Json::Value compile_code;
            compile_code["code"] = in_value["code"].asString() + "\n" +  q.test;
            compile_code["input"] = in_value["input"].asString();
            compile_code["cpu_limit"] = q.cpu_limit;
            compile_code["mem_limit"] = q.mem_limit;
            Json::FastWriter writer;
            std::string out_string = writer.write(compile_code);
            //负载均衡
            while(true)
            {
                int id = 0;
                machine* m;
                if(!_load_balance.SmartChoice(&id, &m))
                {
                    break;
                }
                Client cli(m->ip, m->port);
                m->IncLoad();
                LOG(INFO) << "选择主机成功, 主机id是 " << id  << " 详情 : " << m->ip << "," << m->port << ",负载: " << m->load << std::endl;
                if(auto res = cli.Post("/compile_run", out_string, "application/json;charset=utf-8"))
                {
                    if(res->status == 200)
                    {
                        *out_json = res->body;
                        m->DecLoad();
                        LOG(INFO) << "请求编译和运行服务成功..." << std::endl;
                        break;
                    }
                    m->DecLoad();
                }
                else
                {
                    LOG(ERROR) << "选择主机失败, 主机id是 " << id  << " 详情 : " << m->ip << "," << m->port  << ",可能已经离线了"<< std::endl;
                    _load_balance.OfflineMachine(id);
                    _load_balance.ShowMachine(); //for debug
                }
            }
        }
    };
}
